package org.ns.summer.mvc;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.ns.summer.beans.AnnotationApplicationContext;
import org.ns.summer.beans.ApplicationContext;
import org.ns.summer.core.BeanUtils;
import org.ns.summer.mvc.anns.ResponseBody;
import org.ns.summer.mvc.error.ParamRequiredException;
import org.ns.summer.mvc.mappinghandler.ExecutorResult;
import org.ns.summer.mvc.mappinghandler.HandlerExecutionChain;
import org.ns.summer.mvc.mappinghandler.HandlerMapping;
import org.ns.summer.mvc.mappinghandler.UrlHandlerMapping;
import org.ns.summer.mvc.utils.MvcUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class DispatcherServlet extends HttpServlet {
    public static final String SCAN_PACKAGES = "scanPackages";

    private HandlerMapping handlerMapping ;
    // 简单初始化容器
    @Override
    public void init() throws ServletException {
        ServletConfig servletConfig = getServletConfig();
        String scanPackages = servletConfig.getInitParameter(SCAN_PACKAGES);
        String[] prefixs = MvcUtils.getPrefixs(scanPackages);
        ApplicationContext applicationContext = new AnnotationApplicationContext(DispatcherServlet.class, prefixs);
        handlerMapping = applicationContext.getBean(UrlHandlerMapping.class);
    }



    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doService(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doService(req, resp);
    }

    // 容器请求
    protected void doService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {

            // HandlerMethod handlerMethod = this.handlerMapping;
            HandlerExecutionChain handlerExec = handlerMapping.getHandler(req);
            if (handlerExec == null) {
                resp.setStatus(404);
                return;
            }
            if (handlerExec.getHandlerMethod().existAnnotation(ResponseBody.class)) {
                resp.setContentType("application/json;charset=utf-8");
            } else {
                resp.setContentType("text/html;charset=utf-8");
            }

            if (!handlerExec.preHandler(req, resp)) {
                return;
            }
            // 执行了
            ExecutorResult executorResult = handlerExec.executor(req, resp);


            Object result = executorResult.getResult();
            if (result == null) {
                // 不做处理
            } else if (executorResult.getResponseBody() != null) {
                if (result != null) {
                    PrintWriter writer = resp.getWriter();
                    if (result instanceof CharSequence) {
                        writer.write(result.toString());
                    } else {
                        String json = JSON.toJSONString(result, SerializerFeature.WriteNullStringAsEmpty, // 写字符串 null 作为 "" 处理
                                SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullBooleanAsFalse,
                                SerializerFeature.DisableCircularReferenceDetect);

                        writer.write(json);
                    }
                    writer.flush();
                    writer.close();
                }
            } else {
                String url = MvcUtils.getUrl(result.toString());
                if (!url.endsWith(".jsp")) {
                    // 简单跳转页面
                    String realPath = req.getRealPath(url);
                    MvcUtils.out(resp, realPath);
                } else {
                    req.getRequestDispatcher(url).forward(req, resp);
                }
            }

        } catch (ParamRequiredException pre) {
            pre.printStackTrace();
            resp.setStatus(400);
        } catch (Throwable e) {
            e.printStackTrace();
            resp.setStatus(500);
        }
    }
}
